package com.market.web.model;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Transient;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.market.domain.Location;
import com.market.domain.Order;
import com.market.domain.OrderDetail;
import com.market.domain.PickupSchedule;
import com.market.domain.Product;
import com.market.domain.ProductPrice;
import com.market.domain.Promotion;
import com.market.domain.User;
import com.market.domain.Weekly;
import com.market.domain.transients.MarketDate;

@Component("shoppingCartModel")
@Scope("session")
public class ShoppingCartModel {

	private Boolean isInitialized = Boolean.FALSE;

	private List<Promotion> promotions; // This is all promotions valid at this
										// moment, but for customer to get these
										// promotions,there are some other
										// conditions are needed, like minimum
										// order and correct promotion code;
	private List<Weekly> activeWeeklys = new ArrayList<Weekly>(); // All those
																	// weeks are
																	// set as
																	// active
	private Weekly requiredWeek; // This is the week, customer has chosen
	private User customer;
	private Location location;
	private MarketDate requiredDate;
	private MarketDate newRequiredDate; // Empty basket when user choose a
										// different requiredDate
										// private List<OrderDetail> basket =
										// new ArrayList<OrderDetail>();
	private Order order;
	
	private Boolean isCustomerAcceptedDeliveryFee = Boolean.FALSE;	

	public Boolean getIsCustomerAcceptedDeliveryFee() {
		return isCustomerAcceptedDeliveryFee;
	}


	public void setIsCustomerAcceptedDeliveryFee(
			Boolean isCustomerAcceptedDeliveryFee) {
		this.isCustomerAcceptedDeliveryFee = isCustomerAcceptedDeliveryFee;
	}

	private static final DecimalFormat df = new DecimalFormat("#.00");

	/**
	 * This is to check if we can sell organic food, We buy from George, but they open
	 * Only on Tuesday and Thursday.
	 * @return
	 */
	public Boolean getIsRequiredDayTuesdayOrThursday(){
		return (this.requiredDate.getDayOfWeek() == 3 || this.requiredDate.getDayOfWeek() == 5);
	}
	
	/**
	 * The reason that I put this property here (ShoppingCartModel), not with class Order. Is FreeDeliveryMinimumAmount is
	 * saved with Weekly. I can only get it from this.requiredWeek.getFreeDeliveryMinimumAmount()
	 * @return
	 */
	public Boolean getIsFreeDelivery(){
		Double amount = this.order.getOrderedTotalAmount();
		if(amount >= this.requiredWeek.getFreeDeliveryMinimumAmount()){
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}
	
	
	public Boolean getIsInitialized() {
		return isInitialized;
	}

	public void setIsInitialized(Boolean isInitialized) {
		this.isInitialized = isInitialized;
	}

	public Double getTotalCharge() {
		Double charge = this.order.getOrderedTotalPlusTax()
				- this.customer.getBalance();
		if (charge < 0) {
			return 0D;
		}
		return charge;
	}

	public String getTotalChargeLabel() {
		return df.format(this.getTotalCharge());
	}

	public Order getOrder() {
		return order;
	}

	public void setOrder(Order order) {
		this.order = order;
	}

	public MarketDate getNewRequiredDate() {
		return newRequiredDate;
	}

	public void setNewRequiredDate(MarketDate newRequiredDate) {
		this.newRequiredDate = newRequiredDate;
	}

	public MarketDate getRequiredDate() {
		return this.requiredDate;
	}

	/**
	 * Set the required date, at the some time set required Week
	 * 
	 * @param requiredDate
	 */

	public void setRequiredDate(MarketDate requiredDate) {
		this.requiredDate = requiredDate;
		for (Weekly weekly : this.activeWeeklys) {
			if (weekly.getYear().equals(this.requiredDate.getYear())
					&& weekly.getWeekOfYear().equals(
							this.requiredDate.getWeekOfYear())) {
				this.requiredWeek = weekly;
				break;
			}
		}
	}

	public Weekly getRequiredWeek() {
		return requiredWeek;
	}

	public void setRequiredWeek(Weekly requiredWeek) {
		this.requiredWeek = requiredWeek;
	}

	public List<OrderDetail> getBasket() {
		if (this.order != null && this.order.getOrderDetails() != null) {
			return this.order.getOrderDetails();
		}
		return null;
	}

	/**
	 * Return if there is some promotion can be applied to shopping cart, if so
	 * there would be another row showing how much credit reward the customer
	 * can get, if they submit this order, or this row would be hided.
	 * 
	 * @return
	 */
	public Boolean getHavePromotionReward() {
		this.order.setPromotions(this.getValidPromotions());
		return this.order.getPromotionRewardRate() > 0;
	}

	public Boolean getHaveEnoughCredit() {
		if (this.customer.getBalance() >= this.order.getOrderedTotalPlusTax()) {
			return Boolean.TRUE;
		} else {
			return Boolean.FALSE;
		}
	}

	/**
	 * Return all promotions can be applied to this order Conditions include
	 * minimum order amount and valid promotion code, and ordered date is valid
	 * too.
	 * 
	 * @return
	 */

	public List<Promotion> getValidPromotions() {
		List<Promotion> validPromotions = new ArrayList<Promotion>();
		Double total = this.order.getOrderedTotalAmount();
		for (Promotion promotion : this.promotions) {
			if (promotion.getMinAmount() <= total) {
				promotion.setIsMinAmountValid(true);
				if (promotion.getIsCodeValid()) {
					validPromotions.add(promotion);
				}
			} else {
				promotion.setIsMinAmountValid(false);
			}

		}
		return validPromotions;
	}

	/**
	 * This method does nothing, just for setAdd(Product product) to work
	 * properly, Use this to <h:commandButton>'s actionListener
	 * 
	 */
	public void add() {
		// Product product = productModel.getCurrent();
		// this.add(product);
	}

	/**
	 * Add one product to basket example:
	 * <p:commandButton value="Buy" actionListener="#{shoppingCart.add}" update=":rightForm">
	 * <f:setPropertyActionListener value="#{product}"
	 * target="#{shoppingCart.add}" />
	 * </p:commandButton>
	 * 
	 * @param product
	 */

	public void setAdd(ProductPrice productPrice) {

		boolean isExist = false;
		for (OrderDetail orderDetail : order.getOrderDetails()) {
			if (orderDetail.getProduct().equalsId(productPrice.getProduct())) {
				orderDetail.setOrderedQuantity(orderDetail.getOrderedQuantity()
						+ productPrice.getProduct().getQuantity());
				isExist = true;
				break;
			}
		}
		if (isExist == false) {
			order.getOrderDetails().add(this.createOrderDetail(productPrice));
		}
	}

	private OrderDetail createOrderDetail(ProductPrice productPrice) {
		OrderDetail orderDetail = new OrderDetail();
		orderDetail.setIsNew(true);
		orderDetail.setProduct(productPrice.getProduct());
		orderDetail.setSpecification(productPrice.getProduct()
				.getSpecification());
		orderDetail.setOrderedQuantity(productPrice.getProduct().getQuantity());
		// Use productPrice's special price, not product's special price. This
		// two figure maybe different.
		orderDetail.setOrderedPrice(productPrice.getSpecialPrice());
		if (productPrice.getProduct().getIsTaxable()) {
			orderDetail.setGstRate(0.05D);
			orderDetail.setQstRate(0.09975D);
		}
		return orderDetail;
	}

	/**
	 * This method does nothing, just for setRemove to work properly, Use this
	 * on <h:commandButton>'s actionListener.
	 */

	public void remove() {
		// Product product = productModel.getCurrent();
		// this.remove(product);
	}

	/**
	 * Remove one product from basket; Example:
	 * <p:commandButton actionListener="#{shoppingCart.remove}" icon="delete" title="#{msgs.deleteCurrent}" ajax="TRUE" update=":rightForm" immediate="true" >
	 * <f:setPropertyActionListener target="#{shoppingCart.remove}"
	 * value="#{orderDetail.product}"></f:setPropertyActionListener>
	 * </p:commandButton>
	 * 
	 * @param product
	 */

	public void setRemove(Product product) {
		for (int i = 0; i < order.getOrderDetails().size(); i++) {
			if (order.getOrderDetails().get(i).getProduct().equalsId(product)) {
				order.getOrderDetails().remove(i);
			}
		}

	}

	public void emptyBasket() {
		this.order.getOrderDetails().clear();
		// this.requiredDate = null;
	}

	public Boolean getAvailablePickupDatesEmpty() {
		return this.getAvailablePickupDates().size() <= 0;
	}

	/**
	 * All available pick-up date for customer. Use this when customer order
	 * from normal order interface.
	 * 
	 * After 10:00AM customer can not choose that day anymore.
	 * 
	 * @return
	 */
	public List<MarketDate> getAvailablePickupDates() {
		List<MarketDate> dates = new ArrayList<MarketDate>();
		for (Weekly weekly : activeWeeklys) {
			for (PickupSchedule pickupSchedule : this.location
					.getPickupSchedules()) {
				if (pickupSchedule.getIsAvailable() == Boolean.TRUE) {
					MarketDate marketDate = pickupSchedule.getDayOfWeek()
							.getDate(weekly.getYear(), weekly.getWeekOfYear());
					if (marketDate.getIsPasted() == Boolean.FALSE) {
						marketDate.setTimeRangeLabel(pickupSchedule
								.getTimeRangeLabel());
						dates.add(marketDate);
					}
				}
			}
		}
		java.util.Collections.sort(dates);
		return dates;
	}

	/**
	 * All available pick-up dates that our administrator can choose for
	 * customer. Use this when administrator place an order for customer from
	 * administration panel.
	 * 
	 * After 16:00, even administrator can not choose that day any more.
	 * 
	 * @return
	 */

	public List<MarketDate> getAvailablePickupDatesForAdmin() {
		List<MarketDate> dates = new ArrayList<MarketDate>();
		for (Weekly weekly : activeWeeklys) {
			for (PickupSchedule pickupSchedule : this.location
					.getPickupSchedules()) {
				if (pickupSchedule.getIsAvailable() == Boolean.TRUE) {
					MarketDate marketDate = pickupSchedule.getDayOfWeek()
							.getDate(weekly.getYear(), weekly.getWeekOfYear());
					if (marketDate.getIsTruePasted() == Boolean.FALSE) {
						marketDate.setTimeRangeLabel(pickupSchedule
								.getTimeRangeLabel());
						dates.add(marketDate);
					}
				}
			}
		}
		java.util.Collections.sort(dates);
		return dates;
	}

	public Boolean getIsNoPickupDatesAvailable() {
		if (this.getAvailablePickupDates().size() > 0) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	public Boolean getIsBasketEmpty() {
		return (this.order == null || this.order.getOrderDetails().size() <= 0);
	}

	public List<Promotion> getPromotions() {
		if (this.promotions.size() > 0) {
			return promotions;
		} else {
			return null;
		}
	}

	public void setPromotions(List<Promotion> promotions) {
		this.promotions = promotions;
	}

	public List<Weekly> getActiveWeeklys() {
		return activeWeeklys;
	}

	public void setActiveWeeklys(List<Weekly> activeWeeklys) {
		this.activeWeeklys = activeWeeklys;
		if (this.activeWeeklys != null && this.activeWeeklys.size() > 0) {
			this.requiredWeek = this.activeWeeklys.get(0);
		}
	}

	public User getCustomer() {
		return customer;
	}

	public void setCustomer(User customer) {
		if (this.customer == null || !this.customer.equalsId(customer)) {
			this.customer = customer;
			this.isInitialized = Boolean.FALSE;
		}

	}

	public Location getLocation() {
		return location;
	}

	public void setLocation(Location location) {
		this.location = location;
	}

	public Date getOrderedDate() {
		return new Date();
	}

	public Boolean getIsCanConfirm() {
		return this.requiredDate.getCanConfirm();
	}

}
